/*
 * Copyright (c) 2004-2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 *
 * Date: A  April 2011
 */

#include "base/intmath.hh"
#include "base/bitfield.hh"
#include "cpu/edge/pred/tournament_type.hh"
#include "cpu/base.hh"

template<class Impl>
TournamentTypeP<Impl>::TournamentTypeP(uint64_t _localPredictorSize,
                           uint64_t _localHistoryTableSize,
                           uint64_t _localHistoryBits,
                           uint64_t _localCtrBits,
                           uint64_t _globalPredictorSize,
                           uint64_t _globalHistoryBits,
                           uint64_t _globalCtrBits,
                           uint64_t _choiceHistoryBits,
                           uint64_t _choicePredictorSize,
                           uint64_t _choiceCtrBits,
                           uint64_t _blockShiftAmt,
                           std::string _global_index_mode,
                           std::string _prediction_mode,
                           std::string _hysteresis_mode)
    : localPredictorSize(_localPredictorSize),
      localHistoryTableSize(_localHistoryTableSize),
      localHistoryBits(_localHistoryBits),
      localCtrBits(_localCtrBits),
      globalPredictorSize(_globalPredictorSize),
      globalHistoryBits(_globalHistoryBits),
      globalCtrBits(_globalCtrBits),
      choiceHistoryBits(_choiceHistoryBits),
      choicePredictorSize(_choicePredictorSize),
      choiceCtrBits(_choiceCtrBits),
      blockShiftAmt(_blockShiftAmt)
{
    initVars();

    if (_global_index_mode == "Address") {
        globalIndexMode = Address;
    } else if (_global_index_mode == "AddressAndPath") {
        globalIndexMode = AddressAndPath;
    } else if (_global_index_mode == "AddressAndCPath") {
        globalIndexMode = AddressAndCPath;
    } else if (_global_index_mode == "AddressAndHist") {
        globalIndexMode = AddressAndHist;
    } else {
        fatal("Invalid global index mode selected for tournament type"
                " predictor\n");
    }

    if (_prediction_mode == "Local") {
        predictionMode = Local;
    } else if (_prediction_mode == "Global") {
        predictionMode = Global;
    } else if (_prediction_mode == "Tournament") {
        predictionMode = Tournament;
    } else {
        fatal("Invalid prediction mode selected for tournament type"
                " predictor\n");
    }

    if (_hysteresis_mode == "Symmetric") {
        hysteresisMode = Symmetric;
    } else if (_hysteresis_mode == "Asymmetric") {
        hysteresisMode = Asymmetric;
    } else {
        fatal("Invalid hysteresis mode selected for tournament type"
                " predictor\n");
    }
}

template<class Impl>
TournamentTypeP<Impl>::TournamentTypeP(DerivEdgeCPUParams *params)
    : localPredictorSize(params->localPredictorSize),
      localHistoryTableSize(params->localHistoryTableSize),
      localHistoryBits(params->localHistoryBits),
      localCtrBits(params->localCtrBits),
      globalPredictorSize(params->globalPredictorSize),
      globalHistoryBits(params->globalHistoryBits),
      globalCtrBits(params->globalCtrBits),
      choiceHistoryBits(params->choiceHistoryBits),
      choicePredictorSize(params->choicePredictorSize),
      choiceCtrBits(params->choiceCtrBits),
      blockShiftAmt(params->blockShiftAmt)
{
    initVars();

    if (params->btypeTournamentGlobalIndexMode == "Address") {
        globalIndexMode = Address;
    } else if (params->btypeTournamentGlobalIndexMode == "AddressAndPath") {
        globalIndexMode = AddressAndPath;
    } else if (params->btypeTournamentGlobalIndexMode == "AddressAndCPath") {
        globalIndexMode = AddressAndCPath;
    } else if (params->btypeTournamentGlobalIndexMode == "AddressAndHist") {
        globalIndexMode = AddressAndHist;
    } else {
        fatal("Invalid global index mode selected for tournament type"
                " predictor\n");
    }

    if (params->btypeTournamentPredictionMode == "Local") {
        predictionMode = Local;
    } else if (params->btypeTournamentPredictionMode == "Global") {
        predictionMode = Global;
    } else if (params->btypeTournamentPredictionMode == "Tournament") {
        predictionMode = Tournament;
    } else {
        fatal("Invalid prediction mode selected for tournament type"
                " predictor\n");
    }

    if (params->btypeTournamentHysteresisMode == "Symmetric") {
        hysteresisMode = Symmetric;
    } else if (params->btypeTournamentHysteresisMode == "Asymmetric") {
        hysteresisMode = Asymmetric;
    } else {
        fatal("Invalid hysteresis mode selected for tournament type"
                " predictor\n");
    }
}

template<class Impl>
TournamentTypeP<Impl>::~TournamentTypeP()
{
    DPRINTF(EdgePredUnit,"Tournament destructed!\n");
}

template<class Impl>
void
TournamentTypeP<Impl>::initVars()
{
    if (!isPowerOf2(localPredictorSize)) {
        fatal("Invalid local predictor size!\n");
    }

    /** Setup the array of counters for the local predictor */
    localCtrs.resize(localPredictorSize);

    for (int i = 0; i < localPredictorSize; ++i) {
        localCtrs[i].typeID = 0;
//        localCtrs[i].hysteresis = 0;
	localCtrs[i].hysteresis.setBits(localCtrBits);
    }

    localPredictorMask = floorPow2(localPredictorSize) - 1;

    if (!isPowerOf2(localHistoryTableSize)) {
        fatal("Invalid local history table size!\n");
    }

    /** Setup the history table for the local table */
    localHistoryTable.resize(localHistoryTableSize);

    for (int i = 0; i < localHistoryTableSize; ++i)
        localHistoryTable[i] = 0;

    /** Setup the history table for the local futrue file */
    localFutureFile.resize(Impl::MaxInFlightBlockNum);

    for (int i = 0; i < Impl::MaxInFlightBlockNum; ++i) {
        localFutureFile[i].index = 0;
	localFutureFile[i].history = 0;
    }
    /** Setup the local history mask */
    localHistoryMask = (ULL(0x1) << localHistoryBits) - 1;

    if (!isPowerOf2(globalPredictorSize)) {
        fatal("Invalid global predictor size!\n");
    }

    /** Setup the array of counters for the global predictor */
    globalCtrs.resize(globalPredictorSize);

    for (int i = 0; i < globalPredictorSize; ++i) {
//        globalCtrs[i].hysteresis = 0;
        globalCtrs[i].hysteresis.setBits(globalCtrBits);
	globalCtrs[i].typeID = 0;
    }

    /** Clear the global history */
    for (int i = 0; i < Impl::MaxThreads ;++i ) {
	globalHistory[i] = 0;
        pathHistoryTable[i].resize(globalHistoryBits, static_cast<Addr>(0));
    }

    globalPredictorMask = floorPow2(globalPredictorSize) - 1;
    /** Setup the global history mask */
    globalHistoryMask = (ULL(0x1) << globalHistoryBits) - 1;

    globalHistoryFile.resize(Impl::MaxInFlightBlockNum);
	
    for (int i = 0; i < Impl::MaxInFlightBlockNum ;++i) {
	globalHistoryFile[i] = 0;
        pathHistoryFile[i].resize(globalHistoryBits, static_cast<Addr>(0));
    }

    choiceHistoryFile.resize(Impl::MaxInFlightBlockNum);
	
    for (int i = 0; i < Impl::MaxInFlightBlockNum ;++i )
	choiceHistoryFile[i] = 0;
	
    if (!isPowerOf2(choicePredictorSize)) {
        fatal("Invalid choice predictor size!\n");
    }
    /** Clear the choice history */
    for (int i = 0; i < Impl::MaxThreads; ++i)
        choiceHistory[i] = 0;

    choicePredictorMask = floorPow2(choicePredictorSize) - 1;

    /** Setup the choice history mask */
    choiceHistoryMask = (ULL(0x1) << choiceHistoryBits) - 1;
    
    /** Setup the array of counters for the choice predictor */
    choiceCtrs.resize(choicePredictorSize);

    for (int i = 0; i < choicePredictorSize; ++i)
        choiceCtrs[i].setBits(choiceCtrBits);

    threshold = (ULL(0x1) << (choiceCtrBits - 1)) - 1;
    threshold = threshold / 2;

    glThreshold = (ULL(0x1) << (globalCtrBits - 1)) - 1;
    glThreshold = glThreshold / 2;

    typeIdBits = floorLog2(TheISA::NumBranchType);

//    typeIdMask = static_cast<uint64_t>(typeIdBits) - 1;
    typeIdMask = (ULL(0x1) << typeIdBits) - 1;

    DPRINTF(EdgePredUnit, "TournamentTypeP: type id bits: %i"
            " type id mask: %#x\n",
            typeIdBits, typeIdMask);
}

template<class Impl>
void
TournamentTypeP<Impl>::reset()
{
    for (int i = 0; i < localPredictorSize; ++i) {
        localCtrs[i].typeID = 0;
//        localCtrs[i].hysteresis = 0;
	localCtrs[i].hysteresis.reset();
    }
	
    for (int i = 0; i < globalPredictorSize; ++i) {
//        globalCtrs[i].hysteresis = 0;
        globalCtrs[i].hysteresis.reset();
	globalCtrs[i].typeID = 0;
    }
	
    //Clear the global history
    for (int i = 0; i < Impl::MaxThreads ;++i) {
	globalHistory[i] = 0;

        for (int j = 0; j < globalHistoryBits; j++) {
            pathHistoryTable[i][j] = 0;
        }
    }
	
    for (int i = 0; i < Impl::MaxInFlightBlockNum ;++i) {
	globalHistoryFile[i] = 0;

        for (int j = 0; j < globalHistoryBits; j++) {
            pathHistoryFile[i][j] = 0;
        }
    }
	
    for (int i = 0; i < Impl::MaxInFlightBlockNum ;++i )
	choiceHistoryFile[i] = 0;
	
    //Clear the choice history
    for (int i = 0; i < Impl::MaxThreads; ++i)
        choiceHistory[i] = 0;
    
    for (int i = 0; i < choicePredictorSize; ++i)
        choiceCtrs[i].setBits(choiceCtrBits);    
}

template<class Impl>
inline
uint64_t
TournamentTypeP<Impl>::calcLocHistIdx(Addr &block_addr,int addr_space_ID)
{
    // Get low order bits after removing instruction offset.
    assert(addr_space_ID >= 0 && addr_space_ID <= 15);

    int reverse_as_id = ((addr_space_ID & 0x1) << 3)
                        | ((addr_space_ID & 0x2) << 1)
                        | ((addr_space_ID & 0x4) >> 1)
                        | ((addr_space_ID & 0x8) >> 3);
    return ((block_addr >> blockShiftAmt) ^ (reverse_as_id << (localHistoryBits -4))) 
		& (localHistoryTableSize - 1);
}

template<class Impl>
inline
uint64_t
TournamentTypeP<Impl>::calcLocPredIdx(Addr &block_addr, uint64_t history)
{
    uint64_t index = (history ^ (block_addr >> blockShiftAmt)) & localPredictorMask;
    return index;
}

template<class Impl>
inline
uint64_t
TournamentTypeP<Impl>::calcGloPredIdx(Addr &block_addr, uint64_t history,
        uint64_t path_hist)
{
    uint64_t index = ULL(0xffffffffffffffff);
    
    if (globalIndexMode == Address) {

        index = (block_addr >> blockShiftAmt) & globalPredictorMask;

    } else if (globalIndexMode == AddressAndPath) {

        index = ((block_addr >> blockShiftAmt) ^
                (path_hist & globalHistoryMask)) & globalPredictorMask;

    } else if (globalIndexMode == AddressAndCPath) {

        uint64_t pattern = bits(path_hist, (globalHistoryBits / 2) - 1, 0);

        index = (((block_addr >> blockShiftAmt) << (globalHistoryBits / 2)) |
            pattern) & globalPredictorMask;

    } else if (globalIndexMode == AddressAndHist) {

        index = (history ^ (block_addr >> blockShiftAmt))
            & globalPredictorMask;

    } else {
        panic("Invalid global index mode selected for tournament type"
                " predictor\n");
    }

    return index;
}

template<class Impl>
inline
uint64_t
TournamentTypeP<Impl>::calcChoiPredIdx(Addr &block_addr,uint64_t history)
{
    uint64_t index = (history ^ (block_addr >> blockShiftAmt)) & choicePredictorMask;
    return index;
}

template<class Impl>
uint64_t
TournamentTypeP<Impl>::lookupGetIndex(Addr &block_pc, ThreadID tid)
{
    uint64_t choice_history = choiceHistory[tid] & choiceHistoryMask;
    uint64_t choice_predictor_idx = calcChoiPredIdx(block_pc, choice_history);
    return choice_predictor_idx;
}

template<class Impl>
uint64_t
TournamentTypeP<Impl>::updateGetIndex(BlockID block_id, Addr &block_pc, ThreadID tid)
{
    uint64_t index = block_id & (Impl::MaxInFlightBlockNum - 1);
    uint64_t choice_predictor_hist = choiceHistoryFile[index];
    uint64_t choice_predictor_idx = calcChoiPredIdx(block_pc, choice_predictor_hist);
    return choice_predictor_idx;
}

template<class Impl>
TheISA::BTypeID
TournamentTypeP<Impl>::lookup(BlockID block_id, BlockID oldest_block_id, 
        Addr &block_addr, int addr_space_ID, ThreadID tid)
{
    BTypeID local_prediction = TheISA::InvalidExitType;
    uint64_t local_history_idx = 0;
    uint64_t local_history = 0;
    uint64_t local_predictor_idx = 0;

    uint64_t global_history = 0;
    uint64_t path_history = 0;
    uint64_t global_predictor_idx = 0;
    BTypeID global_prediction = TheISA::InvalidExitType;

    uint64_t choice_history = 0 ;
    uint64_t choice_predictor_idx = 0;
    bool choice_prediction = false;
    BTypeID prediction = TheISA::InvalidExitType;

    uint64_t global_history_new = 0;
    uint64_t choice_history_new = 0;
    LFFEntry lff_entry_new;

    if (predictionMode == Local || predictionMode == Tournament) {
        //Get local history.
        local_history_idx = calcLocHistIdx(block_addr, addr_space_ID);
        local_history = localHistoryTable[local_history_idx];

        if (block_id != oldest_block_id && oldest_block_id != 0) {
            BlockID tmp_bid = block_id; // latest block

            do {
                LFFEntry lff_entry;
                tmp_bid = (tmp_bid - 1) & 0x7;
                lff_entry = localFutureFile[tmp_bid];
                if (lff_entry.index == local_history_idx)
                {
                    local_history = lff_entry.history;
                    break;
                }
            } while (tmp_bid != (oldest_block_id & 0x7));
        }

        //Lookup local prediction
        local_predictor_idx = calcLocPredIdx(block_addr, local_history);
        local_prediction = localCtrs[local_predictor_idx].typeID;
        
        prediction = local_prediction;
    }

    if (predictionMode == Global || predictionMode == Tournament) {
        //Get global history
        global_history = globalHistory[tid] & globalHistoryMask;
        path_history = getPathHistory(tid);

        global_predictor_idx = calcGloPredIdx(block_addr, global_history,
                path_history);
        
        //Lookup in the global predictor to get its branch prediction
        global_prediction = globalCtrs[global_predictor_idx].typeID;

        prediction = global_prediction;
    }

    if (predictionMode == Tournament) {
        //Get choice history
        choice_history = choiceHistory[tid] & choiceHistoryMask;
        choice_predictor_idx = calcChoiPredIdx(block_addr,choice_history);
        //Lookup in the choice predictor to see which one to use
        choice_prediction = choiceCtrs[choice_predictor_idx].read() > threshold;

        // Final prediction
        prediction = choice_prediction ? global_prediction : local_prediction;
    }

    DPRINTF(EdgePredUnit,"BP: global prediction = %i, local_prediction = %i"
            " for %#x.\n",
            global_prediction,local_prediction,block_addr);

    uint64_t index = block_id & (Impl::MaxInFlightBlockNum - 1); 

    if (predictionMode == Global || predictionMode == Tournament) {
        // Speculative update
        globalHistoryFile[index] = global_history;

        global_history_new = ((global_history << typeIdBits) |
                (global_prediction & typeIdMask)) &
                globalHistoryMask ;

        globalHistory[tid] = global_history_new;

        PathHistory path = pathHistoryTable[tid];
        pathHistoryFile[index] = pathHistoryTable[tid];
        updatePathHistory(path, block_addr);
        pathHistoryTable[tid] = path;
    }

    if (predictionMode == Tournament) {
        // Speculative update
        choiceHistoryFile[index] = choice_history;
        choice_history_new = ((choice_history << typeIdBits) |
                (prediction & typeIdMask)) &
                choiceHistoryMask ;
        choiceHistory[tid] = choice_history_new;
    }

    if (predictionMode == Local || predictionMode == Tournament) {
        // Speculative update
        lff_entry_new.index = local_history_idx;
        lff_entry_new.history = ((local_history << typeIdBits) |
                (local_prediction & typeIdMask)) &
                localHistoryMask;
        localFutureFile[index] = lff_entry_new;
    }

    return prediction;
}

template<class Impl>
void
TournamentTypeP<Impl>::update(BlockID block_id, Addr &block_addr, 
        int addr_space_id, BTypeID actual_type_id, ThreadID tid)
{
    uint64_t local_history_idx = 0;
    uint64_t local_predictor_hist = 0;
    uint64_t local_predictor_hist_new = 0;
    uint64_t global_predictor_hist = 0;
    uint64_t path_hist = 0;
    uint64_t choice_predictor_hist = 0;
    uint64_t local_predictor_idx = 0;
    uint64_t global_predictor_idx = 0;
    uint64_t choice_predictor_idx = 0;

//    PredictionTableEntry local_predictor_entry, local_predictor_entry_new;
//    PredictionTableEntry global_predictor_entry, global_predictor_entry_new;
    PredictionTableEntry local_predictor_entry;
    PredictionTableEntry global_predictor_entry;

    assert(actual_type_id >= 0 && actual_type_id < TheISA::NumBranchType);

    // Read L1 tables of exit predictor
    uint64_t index = block_id & (Impl::MaxInFlightBlockNum -1);

    if (predictionMode == Local || predictionMode == Tournament) {
        local_history_idx = calcLocHistIdx(block_addr, addr_space_id);
        local_predictor_hist = localHistoryTable[local_history_idx];

        //Update L1 tables
        local_predictor_hist_new = ((local_predictor_hist << typeIdBits) |
                                   (actual_type_id & typeIdMask)) & localHistoryMask;

        localHistoryTable[local_history_idx] = local_predictor_hist_new;
        
        // Read and update L2 tables
        local_predictor_idx = calcLocPredIdx(block_addr, local_predictor_hist);
        local_predictor_entry = localCtrs[local_predictor_idx];

//        local_predictor_entry_new.typeID = local_predictor_entry.hysteresis ?
//            local_predictor_entry.typeID : actual_type_id ;

        updateHysteresis(localCtrs[local_predictor_idx], actual_type_id);

//        local_predictor_entry_new.hysteresis =
//            (local_predictor_entry.typeID == actual_type_id);

//        localCtrs[local_predictor_idx] = local_predictor_entry_new;
    }

    if (predictionMode == Global || predictionMode == Tournament) {
        global_predictor_hist = globalHistoryFile[index];
        path_hist = getPathHistory(block_id, tid);
            
        global_predictor_idx = calcGloPredIdx(block_addr, global_predictor_hist,
                path_hist);

        global_predictor_entry = globalCtrs[global_predictor_idx];

        updateHysteresis(globalCtrs[global_predictor_idx], actual_type_id);

//        global_predictor_entry_new.typeID = global_predictor_entry.hysteresis ?
//            global_predictor_entry.typeID : actual_type_id ;

//        global_predictor_entry_new.hysteresis =
//            (global_predictor_entry.typeID == actual_type_id);

//        globalCtrs[global_predictor_idx] = global_predictor_entry_new;
    }

    if (predictionMode == Tournament) {
        choice_predictor_hist = choiceHistoryFile[index];

        choice_predictor_idx = calcChoiPredIdx(block_addr, choice_predictor_hist);

        if (local_predictor_entry.typeID != global_predictor_entry.typeID) {
            if(local_predictor_entry.typeID == actual_type_id) {
                choiceCtrs[choice_predictor_idx].decrement();
            } else if(global_predictor_entry.typeID == actual_type_id) {
                choiceCtrs[choice_predictor_idx].increment();
            }
        }
    }

    DPRINTF(EdgePredUnit,"BPUpdate: typeID = %i ,global predictor = %i(%i), "
		"and local predictor = %i(%i).\n", actual_type_id,
		globalCtrs[global_predictor_idx].typeID,
                global_predictor_entry.typeID,
		localCtrs[local_predictor_idx].typeID,
                local_predictor_entry.typeID);
}

template<class Impl>
void
TournamentTypeP<Impl>::squash(BlockID block_id, Addr block_pc, 
        BTypeID type_id, ThreadID tid)
{
    uint64_t global_history = 0;
    uint64_t choice_history = 0;
    uint64_t index = block_id & (Impl::MaxInFlightBlockNum - 1) ;

    if (predictionMode == Local || predictionMode == Tournament) {
        LFFEntry oldLFFEntry, newLFFEntry;
        oldLFFEntry = localFutureFile[index];
        newLFFEntry.index = oldLFFEntry.index;
        newLFFEntry.history = ((oldLFFEntry.history & (~typeIdMask)) |
                (type_id & typeIdMask))
                & localHistoryMask;
        localFutureFile[index] = newLFFEntry;
    }

    if (predictionMode == Global || predictionMode == Tournament) {
        // Restore global history to state prior to this branch.
        global_history = globalHistoryFile[index];
        globalHistory[tid] = ((global_history << typeIdBits) | (type_id & typeIdMask))
            & globalHistoryMask ;

        PathHistory path = pathHistoryFile[index];
        updatePathHistory(path, block_pc);
        pathHistoryTable[tid] = path;
    }

    if (predictionMode == Tournament) {
        choice_history = choiceHistoryFile[index];
        choiceHistory[tid] = ((choice_history << typeIdBits) | (type_id & typeIdMask))
            & choiceHistoryMask ;
    }

    DPRINTF(EdgePredUnit,"Squashing BP to Block [bid:%i].\n",block_id);
    // Delete this BPHistory now that we're done with it.
}

template<class Impl>
void
TournamentTypeP<Impl>::squash(BlockID block_id, ThreadID tid)
{
    uint64_t index = block_id & (Impl::MaxInFlightBlockNum - 1);
    uint64_t global_history = 0;
    uint64_t choice_history = 0;

    if (predictionMode == Global || predictionMode == Tournament) {
        global_history = globalHistoryFile[index];
        globalHistory[tid] = global_history;
        pathHistoryTable[tid] = pathHistoryFile[index];
    }

    if (predictionMode == Tournament) {
        choice_history = choiceHistoryFile[index];
        choiceHistory[tid] = choice_history;
    }
}

template<class Impl>
void 
TournamentTypeP<Impl>::updatePathHistory(PathHistory &path_history, Addr new_pc)
{
    DPRINTF(EdgePredUnit, "Update path history,"
            " Pop: %#x; Push: %#x\n",
            path_history.back(),
            new_pc);

    path_history.pop_back();

    new_pc >>= blockShiftAmt;

    path_history.push_front(new_pc);
}

template<class Impl>
uint64_t
TournamentTypeP<Impl>::getPathHistory(BlockID block_id, ThreadID tid)
{
    int speculative_frame = block_id & (Impl::MaxInFlightBlockNum - 1);

    PathHistory path = pathHistoryFile[speculative_frame];
    uint64_t path_hist = 0;

    for (int i = 0; i < path.size(); i++) {
        if (i < 16) {
            // Take 4 bits from each path address
            path_hist |= ((path[i] & ULL(0xF)) << (i * 4));
        }
    }

    return path_hist;
}

template<class Impl>
uint64_t
TournamentTypeP<Impl>::getPathHistory(ThreadID tid)
{
    PathHistory path = pathHistoryTable[tid];
    uint64_t path_hist = 0;

    for (int i = 0; i < path.size(); i++) {
        if (i < 16) {
            // Take 4 bits from each path address
            path_hist |= ((path[i] & ULL(0xF)) << (i * 4));
        }
    }

    return path_hist;
}

template<class Impl>
void
TournamentTypeP<Impl>::updateHysteresis(PredictionTableEntry &entry,
        BTypeID actual_type_id)
{
    assert(actual_type_id >= 0 && actual_type_id < TheISA::NumBranchType);

    BTypeID current_type_id = entry.typeID;

    if (hysteresisMode == Symmetric) {
        if (entry.hysteresis.read() <= glThreshold) {
            entry.typeID = actual_type_id;
        }

        if (actual_type_id == current_type_id) {
            entry.hysteresis.increment();
        } else {
            entry.hysteresis.decrement();
        }
    } else if (hysteresisMode == Asymmetric) {
        if (entry.hysteresis.read() <= glThreshold) {
            entry.typeID = actual_type_id;
        }

        if (actual_type_id == current_type_id) {
            if (actual_type_id == TheISA::call ||
                    actual_type_id == TheISA::ret) {
                entry.hysteresis.increment((1 << globalCtrBits) - 1);
            } else {
                entry.hysteresis.increment();
            }
        } else {
            if (actual_type_id == TheISA::call ||
                    actual_type_id == TheISA::ret) {
                entry.hysteresis.decrement((1 << globalCtrBits) - 1);
            } else {
                entry.hysteresis.decrement();
            }
        }
    } else {
        panic("Invalid hysteresis mode selected.\n");
    }
}

